use actix_web::{web, HttpResponse, Responder};
use serde::{Deserialize, Serialize};
use std::fs::{read_dir, File};
use std::io::Write;
use std::path::Path;
use std::os::unix::fs::PermissionsExt;
use rusqlite::{params, Connection};
use std::fs;
use walkdir::WalkDir;
use crate::pb::{agent_client::AgentClient,SyncRequest,File as ConfigFile};

const HOOK_CONTENT: &str = include_str!("../assets/post-commit-hook.sh");

pub async fn register_config(body: web::Json<RegisterConfigRequest>)->impl Responder{
    let dir = match read_dir(&body.path){
        Ok(dir) => dir,
        Err(e) => return HttpResponse::BadRequest().body(format!("Error reading directory: {:?}",e)),
    };
    let mut is_exist_apply_script =false;
    for entry in dir{
        let entry = entry.unwrap();
        let path = entry.path();
        if path.is_file()&&path.file_name().unwrap()=="apply.sh"{
            is_exist_apply_script=true;
            break;
        }
    }
    if !is_exist_apply_script{
        return HttpResponse::BadRequest().body("apply.sh not found in the directory");
    }
    let repo_dir=Path::new(&body.path);
    let file_path=repo_dir.join(".git").join("hooks").join("post-commit");
    let mut file = File::create(file_path).unwrap();
    file.write_all(HOOK_CONTENT.as_bytes()).unwrap();
    let metadata = file.metadata().unwrap();
    let mut permissions = metadata.permissions();
    permissions.set_mode(0o755);
    file.set_permissions(permissions).unwrap();

    let conn = Connection::open("server_database.db").unwrap();
    match conn.execute(
        "INSERT INTO configs (path) VALUES (?1)",
        params![body.path],
    ){
        Ok(_) => HttpResponse::Ok().body("config registered"),
        Err(e) => HttpResponse::BadRequest().body(format!("Error registering config: {:?}",e)),
    }

}

#[derive(Deserialize, Serialize, Debug)]
pub struct RegisterConfigRequest {
    name: String,
    path: String,
}

pub async fn notify(body:web::Json<NotifyRequest>)->impl Responder{
    sync(body.name.clone(), body.repo_path.clone()).await;
    HttpResponse::Ok().body("Notified")
}

#[derive(Deserialize, Serialize, Debug)]
pub struct NotifyRequest {
    name:String,
    repo_path: String,
    commit_hash: String,
    commit_message: String,
}

#[derive(Debug)]
pub struct Config {
    pub id: i32,
    pub name: String,
    pub path: String,
}

pub async fn test_db()->impl Responder{
    let conn = Connection::open("my_database.db").unwrap();
    let mut stmt = conn.prepare("SELECT id, name, path FROM configs").unwrap();
    let configs = stmt
        .query_map(params![], |row| {
            Ok(Config {
                id: row.get(0)?,
                name: row.get(1)?,
                path: row.get(2)?,
            })
        }).unwrap();
    
    for config in configs{
        println!("{:?}",config.unwrap());
    }
    "Check the logs"
}

async fn sync(config:String,path:String){
    let conn = Connection::open("server_database.db").unwrap();
    let mut stmt = conn.prepare("SELECT addr FROM agents WHERE config = ?").unwrap();
    let agents = stmt
        .query_map(params![config], |row| {
            Ok(row.get::<_, String>(0)?)
        }).unwrap();
        
    let path_clone = path.clone();
    // get files from path without .git
    let files = WalkDir::new(path)
        .into_iter()
        .filter_map(|e| e.ok())
        .filter(|e| e.path().is_file())
        .filter(|e| e.path().to_str().unwrap().contains(".git") == false)
        .map(|e| {
            let path = e.path().to_str().unwrap().to_string();
            let content = fs::read(e.path()).unwrap();
            ConfigFile{
                path,
                content,
            }
        }).collect::<Vec<ConfigFile>>();

    for agent in agents{
        let agent = agent.unwrap();
        let mut client = AgentClient::connect(format!("http://{}",agent)).await.unwrap();
        let request = tonic::Request::new(SyncRequest{
            path: path_clone.clone(),
            files: files.clone(),
        });
        let response = client.sync(request).await.unwrap().into_inner();
        println!("Response: {:?}",response);
    }
}

#[derive(Deserialize, Serialize, Debug)]
pub struct AgentRequest {
    addr: String,
    config: String,
}
pub async fn agent(body:web::Json<AgentRequest>)->impl Responder{
    let conn = Connection::open("server_database.db").unwrap();
    match conn.execute(
        "INSERT INTO agents (addr, config) VALUES (?1, ?2)",
        params![body.addr, body.config],
    ){
        Ok(_) => HttpResponse::Ok().body("Agent registered"),
        Err(e) => HttpResponse::BadRequest().body(format!("Error registering agent: {:?}",e)),
    }
}